package com.jinglin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jinglin.component.EsSearchComponent;
import com.jinglin.entity.constants.Constants;
import com.jinglin.entity.enums.PageSize;
import com.jinglin.entity.enums.ResponseCodeEnum;
import com.jinglin.entity.enums.SearchOrderTypeEnum;
import com.jinglin.entity.enums.UserActionTypeEnum;
import com.jinglin.entity.po.UserAction;
import com.jinglin.entity.po.VideoComment;
import com.jinglin.entity.po.VideoInfo;
import com.jinglin.entity.query.SimplePage;
import com.jinglin.entity.query.UserActionQuery;
import com.jinglin.entity.vo.PaginationResultVO;
import com.jinglin.exception.BusinessException;
import com.jinglin.mapper.UserActionMapper;
import com.jinglin.mapper.UserInfoMapper;
import com.jinglin.mapper.VideoCommentMapper;
import com.jinglin.mapper.VideoInfoMapper;
import com.jinglin.service.UserActionService;
import com.jinglin.service.VideoCommentService;
import com.jinglin.service.VideoInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * <p>
 * 用户行为 点赞、评论 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2025-03-24
 */
@Service
public class UserActionServiceImpl extends ServiceImpl<UserActionMapper, UserAction> implements UserActionService {
    @Autowired
    private VideoInfoService videoInfoService;
    @Autowired
    private VideoInfoMapper videoInfoMapper;
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Autowired
    private VideoCommentMapper videoCommentMapper;
    @Autowired
    private UserActionMapper userActionMapper;
/*    @Autowired
    private EsSearchComponent esSearchComponent;*/

    @Override
    @Transactional
    public void saveAction(UserAction userAction) {
        VideoInfo videoInfo = videoInfoService.getById(userAction.getVideoId());
        if (videoInfo == null) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        // 从视频信息中获取up主的用户id。存入用户行为表中
        userAction.setVideoUserId(videoInfo.getUserId());
        UserActionTypeEnum actionTypeEnum = UserActionTypeEnum.getByType(userAction.getActionType());
        if (actionTypeEnum == null) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        LambdaQueryWrapper<UserAction> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserAction::getVideoId, userAction.getVideoId());
        wrapper.eq(userAction.getCommentId() != null
                , UserAction::getCommentId, userAction.getCommentId());
        wrapper.eq(UserAction::getActionType, userAction.getActionType());
        wrapper.eq(UserAction::getUserId, userAction.getUserId());
        UserAction dbAction = this.getOne(wrapper);

        switch (actionTypeEnum) {
            case VIDEO_LIKE:
            case VIDEO_COLLECT:
                if (dbAction != null) {
                    // 这个行为在数据库里面已经存在了，那么我这次点击就是删除这个行为
                    // 取消点赞、收藏
                    this.removeById(dbAction.getActionId());
                } else {
                    this.save(userAction);
                }
                // 如果是添加则是1，如果是删除则是-1
                Integer changeCount = dbAction == null ? Constants.ONE : -1;
                videoInfoMapper.updateCountInfo(actionTypeEnum.getField(), changeCount, userAction.getVideoId());
/*                // 更新收藏数量
                esSearchComponent.updateDocCount(videoInfo.getVideoId(), SearchOrderTypeEnum.VIDEO_COLLECT.getField(), changeCount);*/

                break;
            case VIDEO_COIN:
                if (videoInfo.getUserId().equals(userAction.getUserId())) {
                    throw new BusinessException("up主不能给自己的视频投币");
                }
                if (dbAction != null) {
                    // 该行为已存在。（已经投过币了）
                    throw new BusinessException("已投币，请不要重复操作");
                }
                // 先减少自己的币（updateCoinCountInfo有对并发问题的处理）
                Integer countInfo = userInfoMapper.updateCoinCountInfo(userAction.getUserId(), userAction.getActionCount());
                if (countInfo == 0) {
                    throw new BusinessException("用户硬币不够");
                }
                //在给up主加币
                Integer upCountInfo = userInfoMapper.updateCoinCountInfo(videoInfo.getUserId(), userAction.getActionCount());
                if (upCountInfo == 0) {
                    throw new BusinessException("投币失败");
                }
                this.save(userAction);
                // 给视频加硬币数量。
                videoInfoMapper.updateCountInfo(UserActionTypeEnum.VIDEO_COIN.getField(), userAction.getActionCount(), videoInfo.getVideoId());
                break;
            case COMMENT_LIKE:
            case COMMENT_HATE:
                // 对立type
                UserActionTypeEnum oppositeType = ( actionTypeEnum == UserActionTypeEnum.COMMENT_LIKE)
                        ? UserActionTypeEnum.COMMENT_HATE
                        : UserActionTypeEnum.COMMENT_LIKE;


                if (dbAction != null) {
                    // 取消当前操作（点赞或点踩）
                    this.removeById(dbAction.getActionId());

                } else {
                    // 如果是一个全新的操作：那么，有两种情况：
                    // 1. 先删除对立面的数据，再增加本身数据
                    // 2. 直接增加本身数据
                    LambdaQueryWrapper<UserAction> oppositeWrapper = new LambdaQueryWrapper<>();
                    oppositeWrapper.eq(UserAction::getVideoId, userAction.getVideoId())
                            .eq(userAction.getCommentId() != null,
                                    UserAction::getCommentId, userAction.getCommentId())
                            .eq(UserAction::getActionType, oppositeType.getType())
                            .eq(UserAction::getUserId, userAction.getUserId());
                    boolean b = this.remove(oppositeWrapper);
                    if (b){
                        //如果删除成功了，则说明对立面已经被删，则我们需要修改对立操作的评论计数
                        // 具体的意思就是说：我已经点过赞了，这次点踩。那么逻辑是：删除点赞记录，同时修改点赞总计数。然后再新增点踩记录，再修改点踩总计数。
                        videoCommentMapper.updateCountInfo(
                                oppositeType.getField(),
                                userAction.getCommentId(),
                                userAction.getUserId(),
                                -userAction.getActionCount()
                        );
                    }

                    //如果没有删除，则证明没有对立面记录。是一个单纯的新增操作。
                    // 添加当前操作
                    this.save(userAction);
                }
                // 如果记录本身存在，我就去减这个记录。如果不存在，我就去加。
                videoCommentMapper.updateCountInfo(
                        actionTypeEnum.getField(),
                        userAction.getCommentId(),
                        userAction.getUserId(),
                        dbAction != null ? -userAction.getActionCount(): userAction.getActionCount()
                );
                break;

        }


    }

    @Override
    public PaginationResultVO getListByPage(UserActionQuery param) {
        LambdaQueryWrapper<UserAction> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserAction::getActionType,param.getActionType());
        wrapper.eq(UserAction::getUserId,param.getUserId());
        int count = this.count(wrapper);
        int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

        SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
        param.setSimplePage(page);
        List<UserAction> list = userActionMapper.getCollectionPageByUserId(page.getStart(),page.getPageSize(),param.getUserId());
        PaginationResultVO<UserAction> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
        return result;
    }
}
